﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using Godot.core;
using Godot.core.io;

namespace Godot.modules.gdscript
{
    public partial class GDTokenizerBuffer : GDTokenizer
    {		 		
        private List<StringName> _identifiers;
        private List<Variant> _constants;
        private Dictionary<uint, uint> _lines;
        private List<GDTokenizer.Token> _tokens;
        private int _token;        

        public override Variant get_token_constant(int p_offset = 0)
        {
            int offset = _token + p_offset;
            if(offset >= _tokens.Count) 
                return new Variant();
            uint constant = ((uint)_tokens[offset]) >> TOKEN_BITS;
            if(constant >= _constants.Count)
                return new Variant();
            return _constants[(int)constant];
        }

        public override GDTokenizer.Token get_token(int p_offset = 0)
        {
            int offset = _token + p_offset;

	        if (offset < 0 || offset >= _tokens.Count)
		        return GDTokenizer.Token.TK_EOF;

	        return (GDTokenizer.Token)(((int)_tokens[offset]) & TOKEN_MASK);
        }

        public override StringName get_token_identifier(int p_offset = 0)
        {
            int offset = _token + p_offset;

            if(offset >= _tokens.Count) 
                return new StringName();
            
            uint identifier = ((uint)_tokens[offset]) >> TOKEN_BITS;
            
            if(identifier >= _identifiers.Count) 
                return new StringName();

            return _identifiers[(int)identifier];
        }

        public override GDFunctions.Function get_token_built_in_func(int p_offset = 0)
        {
	        int offset = _token + p_offset;
	        if(offset >= _tokens.Count)
                return GDFunctions.Function.FUNC_MAX;
	        return (GDFunctions.Function) (((int)_tokens[offset]) >> TOKEN_BITS);
        }

        public override Variant.Type get_token_type(int p_offset = 0)
        {
            int offset = _token + p_offset;
	        if(offset >= _tokens.Count)
                return Variant.Type.NIL;
    	    return (Variant.Type)(((int)_tokens[offset]) >> TOKEN_BITS);
        }

        public override int get_token_line(int p_offset = 0)
        {
            bool exact = false;
            int offset = _token + p_offset;
            int pos = _lines.find_nearest((uint)offset, ref exact);

            if (pos < 0)
                return -1;

            if (pos >= _lines.Count)
                pos = _lines.Count - 1;

            uint l = _lines[(uint)pos];
            return (int)(l & TOKEN_LINE_MASK);
        }

        public override int get_token_column(int p_offset = 0)
        {
            bool exact = false;
            int offset = _token + p_offset;
            int pos = _lines.find_nearest((uint)offset, ref exact);
            
            if (pos < 0)
                return -1;

            if (pos >= _lines.Count)
                pos = _lines.Count - 1;

            uint l = _lines[(uint)pos];
            return (int)(l >> TOKEN_LINE_BITS);
        }

        public override int get_token_line_indent(int p_offset = 0)
        {
            int offset = _token + p_offset;
            if(offset >= _tokens.Count)
                return 0;
            return ((int)_tokens[offset]) >> TOKEN_BITS;
        }

        public override string get_token_error(int p_offset = 0)
        {
            return "";
        }

        public override void advance(int p_amount = 1)
        {
            if (p_amount + _token >= _tokens.Count)
                return;

	        _token += p_amount;
        }
    }
}
